#pragma once

#ifndef __CORE_Delegate_H__
#define __CORE_Delegate_H__

#include "FLOW_core/namespace.h"
#include "FLOW_core/compile_time_utils.h"
#include "FLOW_core/static_assert.h"
#include "FLOW_core/utils.h"
#include "FLOW_core/null_type.h"
#include <cstring>

namespace CORE
{
	// Define dummy types to take into account all the 
	// posibles alignments and sizes of function pointers in C++
	typedef void generic_fn( void );

	class alignment_dummy_base_1 { };

	class alignment_dummy_base_2 { };

	//Single inheritance
	class alignment_dummy_s : public alignment_dummy_base_1 { };

	//Multiple inheritance
	class alignment_dummy_m : public alignment_dummy_base_1, public alignment_dummy_base_2 { };

	//Virtual inheritance
	class alignment_dummy_v : virtual alignment_dummy_base_1 { };

	//Incomplete type
	class alignment_dummy_u;

	typedef void ( alignment_dummy_s::*m_p_fn_s )( void );
	typedef void ( alignment_dummy_m::*m_p_fn_m )( void );
	typedef void ( alignment_dummy_v::*m_p_fn_v )( void );
	typedef void ( alignment_dummy_u::*m_p_fn_u )( void );

	typedef void ( alignment_dummy_m::*generic_m_p_fn )( void );

	#define NO_CAST_POINTER_MEMBER_FUNCTION_UNRELATED_CLASSES

	template< typename MemberFuncType >
	struct reinterpret_cast_hack
	{
#ifdef NO_CAST_POINTER_MEMBER_FUNCTION_UNRELATED_CLASSES
		union Caster
		{
			MemberFuncType m_p_mfn;
			generic_m_p_fn m_p_gen_mfn;
		};

		static generic_m_p_fn Convert( MemberFuncType ap_mfn )
		{
			Caster c;
			c.m_p_mfn = ap_mfn;
			return c.m_p_gen_mfn;
		}

		static MemberFuncType Restore( generic_m_p_fn ap_gen_mfn )
		{
			Caster c;
			c.m_p_gen_mfn = ap_gen_mfn;
			return c.m_p_mfn;
		}
#else
		static generic_m_p_fn Convert( MemberFuncType ap_mfn )
		{
			STATIC_ASSERT( sizeof( MemberFuncType ) < sizeof( any_function_pointer ), BAD_CASTING );
			return reinterpret_cast< generic_m_p_fn >( ap_mfn );
		}

		static MemberFuncType Restore( generic_m_p_fn ap_gen_mfn )
		{
			STATIC_ASSERT( sizeof( MemberFuncType ) < sizeof( any_function_pointer ), BAD_CASTING );
			return reinterpret_cast< MemberFuncType >( ap_gen_mfn );
		}
#endif
	};

	union max_alignment_for_function_pointer
	{
		const void* dummy_vp;
		generic_fn* dummy_fp;
		ct_if< ( sizeof( generic_m_p_fn ) < sizeof( m_p_fn_s ) ), 
			   generic_m_p_fn, m_p_fn_s >::result dummy_m_p_fn1;
		ct_if< ( sizeof( generic_m_p_fn ) < sizeof( m_p_fn_m ) ), 
			   generic_m_p_fn, m_p_fn_m >::result dummy_m_p_fn2;
		ct_if< ( sizeof( generic_m_p_fn ) < sizeof( m_p_fn_v ) ), 
			   generic_m_p_fn, m_p_fn_v >::result dummy_m_p_fn3;
		ct_if< ( sizeof( generic_m_p_fn ) < sizeof( m_p_fn_u ) ), 
			   generic_m_p_fn, m_p_fn_u >::result dummy_m_p_fn4;
	};

	enum { ANY_FUNCTION_SIZE = sizeof( max_alignment_for_function_pointer ) };

	union any_function_pointer
	{
		const void*							m_p_obj;
		generic_fn*							m_p_free_function;
		generic_m_p_fn						m_p_member_function;
		max_alignment_for_function_pointer	m_alignment;
	};


	/*!
	 * enumerator that defines the different 
	 * types of functions stored in the delegates
	 */
	enum FunctionType
	{
		NULL_FUNC_TYPE,
		FREE_FUNC_TYPE,
		MEMBER_METHOD_TYPE,
	};

// Defines for template specialization
#define RETURN_TYPE					ReturnType
#define RETURN_TYPE_TEMPLATE		typename RETURN_TYPE
#define COMMA_SEPARATOR
#define ARGUMENTS_TYPES				void
#define ARGUMENTS_TYPES_TEMPLATE
#define ARGUMENTS_TYPES_NAMES
#define ARGUMENTS_NAMES
#define ARGUMENTS_SPECIALIZATION

// Set the defines value to create all the specializations needed ( n arguments )
#undef RETURN_TYPE
#undef RETURN_TYPE_TEMPLATE
#undef COMMA_SEPARATOR
#undef ARGUMENTS_TYPES
#undef ARGUMENTS_TYPES_TEMPLATE
#undef ARGUMENTS_TYPES_NAMES
#undef ARGUMENTS_NAMES
#undef ARGUMENTS_SPECIALIZATION

#define RETURN_TYPE					ReturnType
#define RETURN_TYPE_TEMPLATE		typename RETURN_TYPE
#define COMMA_SEPARATOR				,
#define ARGUMENTS_TYPES				Arg1Type, Arg2Type, Arg3Type, Arg4Type, Arg5Type, Arg6Type, Arg7Type, Arg8Type, Arg9Type, Arg10Type
#define ARGUMENTS_TYPES_TEMPLATE	typename Arg1Type = NullType, typename Arg2Type = NullType, typename Arg3Type = NullType, typename Arg4Type = NullType, typename Arg5Type = NullType, typename Arg6Type = NullType, typename Arg7Type = NullType, typename Arg8Type = NullType, typename Arg9Type = NullType, typename Arg10Type = NullType
#define ARGUMENTS_TYPES_NAMES		Arg1Type a1, Arg2Type a2, Arg3Type a3, Arg4Type a4, Arg5Type a5, Arg6Type a6, Arg7Type a7, Arg8Type a8, Arg9Type a9, Arg10Type a10
#define ARGUMENTS_NAMES				a1, a2, a3, a4, a5, a6, a7, a8, a9, a10
#define ARGUMENTS_SPECIALIZATION

#include "Delegate_template.h"

// 0 arguments specialization

#undef RETURN_TYPE
#undef RETURN_TYPE_TEMPLATE
#undef COMMA_SEPARATOR
#undef ARGUMENTS_TYPES
#undef ARGUMENTS_TYPES_TEMPLATE
#undef ARGUMENTS_TYPES_NAMES
#undef ARGUMENTS_NAMES
#undef ARGUMENTS_SPECIALIZATION

#define RETURN_TYPE					ReturnType
#define RETURN_TYPE_TEMPLATE		typename RETURN_TYPE
#define COMMA_SEPARATOR
#define ARGUMENTS_TYPES
#define ARGUMENTS_TYPES_TEMPLATE
#define ARGUMENTS_TYPES_NAMES
#define ARGUMENTS_NAMES
#define ARGUMENTS_SPECIALIZATION	< RETURN_TYPE, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType >

#include "Delegate_template.h"

// 1 arguments specialization

#undef RETURN_TYPE
#undef RETURN_TYPE_TEMPLATE
#undef COMMA_SEPARATOR
#undef ARGUMENTS_TYPES
#undef ARGUMENTS_TYPES_TEMPLATE
#undef ARGUMENTS_TYPES_NAMES
#undef ARGUMENTS_NAMES
#undef ARGUMENTS_SPECIALIZATION

#define RETURN_TYPE					ReturnType
#define RETURN_TYPE_TEMPLATE		typename RETURN_TYPE
#define COMMA_SEPARATOR				,
#define ARGUMENTS_TYPES				Arg1Type
#define ARGUMENTS_TYPES_TEMPLATE	typename Arg1Type
#define ARGUMENTS_TYPES_NAMES		Arg1Type a1
#define ARGUMENTS_NAMES				a1
#define ARGUMENTS_SPECIALIZATION	< RETURN_TYPE, Arg1Type, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType >

#include "Delegate_template.h"

// 2 arguments specialization

#undef RETURN_TYPE
#undef RETURN_TYPE_TEMPLATE
#undef COMMA_SEPARATOR
#undef ARGUMENTS_TYPES
#undef ARGUMENTS_TYPES_TEMPLATE
#undef ARGUMENTS_TYPES_NAMES
#undef ARGUMENTS_NAMES
#undef ARGUMENTS_SPECIALIZATION

#define RETURN_TYPE					ReturnType
#define RETURN_TYPE_TEMPLATE		typename RETURN_TYPE
#define COMMA_SEPARATOR				,
#define ARGUMENTS_TYPES				Arg1Type, Arg2Type
#define ARGUMENTS_TYPES_TEMPLATE	typename Arg1Type, typename Arg2Type
#define ARGUMENTS_TYPES_NAMES		Arg1Type a1, Arg2Type a2
#define ARGUMENTS_NAMES				a1, a2
#define ARGUMENTS_SPECIALIZATION	< RETURN_TYPE, Arg1Type, Arg2Type, NullType, NullType, NullType, NullType, NullType, NullType, NullType, NullType >

#include "Delegate_template.h"

// 3 arguments specialization

#undef RETURN_TYPE
#undef RETURN_TYPE_TEMPLATE
#undef COMMA_SEPARATOR
#undef ARGUMENTS_TYPES
#undef ARGUMENTS_TYPES_TEMPLATE
#undef ARGUMENTS_TYPES_NAMES
#undef ARGUMENTS_NAMES
#undef ARGUMENTS_SPECIALIZATION

#define RETURN_TYPE					ReturnType
#define RETURN_TYPE_TEMPLATE		typename RETURN_TYPE
#define COMMA_SEPARATOR				,
#define ARGUMENTS_TYPES				Arg1Type, Arg2Type, Arg3Type
#define ARGUMENTS_TYPES_TEMPLATE	typename Arg1Type, typename Arg2Type, typename Arg3Type
#define ARGUMENTS_TYPES_NAMES		Arg1Type a1, Arg2Type a2, Arg3Type a3
#define ARGUMENTS_NAMES				a1, a2, a3
#define ARGUMENTS_SPECIALIZATION	< RETURN_TYPE, Arg1Type, Arg2Type, Arg3Type, NullType, NullType, NullType, NullType, NullType, NullType, NullType >

#include "Delegate_template.h"

// 4 arguments specialization

#undef RETURN_TYPE
#undef RETURN_TYPE_TEMPLATE
#undef COMMA_SEPARATOR
#undef ARGUMENTS_TYPES
#undef ARGUMENTS_TYPES_TEMPLATE
#undef ARGUMENTS_TYPES_NAMES
#undef ARGUMENTS_NAMES
#undef ARGUMENTS_SPECIALIZATION

#define RETURN_TYPE					ReturnType
#define RETURN_TYPE_TEMPLATE		typename RETURN_TYPE
#define COMMA_SEPARATOR				,
#define ARGUMENTS_TYPES				Arg1Type, Arg2Type, Arg3Type, Arg4Type
#define ARGUMENTS_TYPES_TEMPLATE	typename Arg1Type, typename Arg2Type, typename Arg3Type, typename Arg4Type
#define ARGUMENTS_TYPES_NAMES		Arg1Type a1, Arg2Type a2, Arg3Type a3, Arg4Type a4
#define ARGUMENTS_NAMES				a1, a2, a3, a4
#define ARGUMENTS_SPECIALIZATION	< RETURN_TYPE, Arg1Type, Arg2Type, Arg3Type, Arg4Type, NullType, NullType, NullType, NullType, NullType, NullType >

#include "Delegate_template.h"

// 5 arguments specialization

#undef RETURN_TYPE
#undef RETURN_TYPE_TEMPLATE
#undef COMMA_SEPARATOR
#undef ARGUMENTS_TYPES
#undef ARGUMENTS_TYPES_TEMPLATE
#undef ARGUMENTS_TYPES_NAMES
#undef ARGUMENTS_NAMES
#undef ARGUMENTS_SPECIALIZATION

#define RETURN_TYPE					ReturnType
#define RETURN_TYPE_TEMPLATE		typename RETURN_TYPE
#define COMMA_SEPARATOR				,
#define ARGUMENTS_TYPES				Arg1Type, Arg2Type, Arg3Type, Arg4Type, Arg5Type
#define ARGUMENTS_TYPES_TEMPLATE	typename Arg1Type, typename Arg2Type, typename Arg3Type, typename Arg4Type, typename Arg5Type
#define ARGUMENTS_TYPES_NAMES		Arg1Type a1, Arg2Type a2, Arg3Type a3, Arg4Type a4, Arg5Type a5
#define ARGUMENTS_NAMES				a1, a2, a3, a4, a5
#define ARGUMENTS_SPECIALIZATION	< RETURN_TYPE, Arg1Type, Arg2Type, Arg3Type, Arg4Type, Arg5Type, NullType, NullType, NullType, NullType, NullType >

#include "Delegate_template.h"

// 6 arguments specialization

#undef RETURN_TYPE
#undef RETURN_TYPE_TEMPLATE
#undef COMMA_SEPARATOR
#undef ARGUMENTS_TYPES
#undef ARGUMENTS_TYPES_TEMPLATE
#undef ARGUMENTS_TYPES_NAMES
#undef ARGUMENTS_NAMES
#undef ARGUMENTS_SPECIALIZATION

#define RETURN_TYPE					ReturnType
#define RETURN_TYPE_TEMPLATE		typename RETURN_TYPE
#define COMMA_SEPARATOR				,
#define ARGUMENTS_TYPES				Arg1Type, Arg2Type, Arg3Type, Arg4Type, Arg5Type, Arg6Type
#define ARGUMENTS_TYPES_TEMPLATE	typename Arg1Type, typename Arg2Type, typename Arg3Type, typename Arg4Type, typename Arg5Type, typename Arg6Type
#define ARGUMENTS_TYPES_NAMES		Arg1Type a1, Arg2Type a2, Arg3Type a3, Arg4Type a4, Arg5Type a5, Arg6Type a6
#define ARGUMENTS_NAMES				a1, a2, a3, a4, a5, a6
#define ARGUMENTS_SPECIALIZATION	< RETURN_TYPE, Arg1Type, Arg2Type, Arg3Type, Arg4Type, Arg5Type, Arg6Type, NullType, NullType, NullType, NullType >

#include "Delegate_template.h"

// 7 arguments specialization

#undef RETURN_TYPE
#undef RETURN_TYPE_TEMPLATE
#undef COMMA_SEPARATOR
#undef ARGUMENTS_TYPES
#undef ARGUMENTS_TYPES_TEMPLATE
#undef ARGUMENTS_TYPES_NAMES
#undef ARGUMENTS_NAMES
#undef ARGUMENTS_SPECIALIZATION

#define RETURN_TYPE					ReturnType
#define RETURN_TYPE_TEMPLATE		typename RETURN_TYPE
#define COMMA_SEPARATOR				,
#define ARGUMENTS_TYPES				Arg1Type, Arg2Type, Arg3Type, Arg4Type, Arg5Type, Arg6Type, Arg7Type
#define ARGUMENTS_TYPES_TEMPLATE	typename Arg1Type, typename Arg2Type, typename Arg3Type, typename Arg4Type, typename Arg5Type, typename Arg6Type, typename Arg7Type
#define ARGUMENTS_TYPES_NAMES		Arg1Type a1, Arg2Type a2, Arg3Type a3, Arg4Type a4, Arg5Type a5, Arg6Type a6, Arg7Type a7
#define ARGUMENTS_NAMES				a1, a2, a3, a4, a5, a6, a7
#define ARGUMENTS_SPECIALIZATION	< RETURN_TYPE, Arg1Type, Arg2Type, Arg3Type, Arg4Type, Arg5Type, Arg6Type, Arg7Type, NullType, NullType, NullType >

#include "Delegate_template.h"

// 8 arguments specialization

#undef RETURN_TYPE
#undef RETURN_TYPE_TEMPLATE
#undef COMMA_SEPARATOR
#undef ARGUMENTS_TYPES
#undef ARGUMENTS_TYPES_TEMPLATE
#undef ARGUMENTS_TYPES_NAMES
#undef ARGUMENTS_NAMES
#undef ARGUMENTS_SPECIALIZATION

#define RETURN_TYPE					ReturnType
#define RETURN_TYPE_TEMPLATE		typename RETURN_TYPE
#define COMMA_SEPARATOR				,
#define ARGUMENTS_TYPES				Arg1Type, Arg2Type, Arg3Type, Arg4Type, Arg5Type, Arg6Type, Arg7Type, Arg8Type
#define ARGUMENTS_TYPES_TEMPLATE	typename Arg1Type, typename Arg2Type, typename Arg3Type, typename Arg4Type, typename Arg5Type, typename Arg6Type, typename Arg7Type, typename Arg8Type
#define ARGUMENTS_TYPES_NAMES		Arg1Type a1, Arg2Type a2, Arg3Type a3, Arg4Type a4, Arg5Type a5, Arg6Type a6, Arg7Type a7, Arg8Type a8
#define ARGUMENTS_NAMES				a1, a2, a3, a4, a5, a6, a7, a8
#define ARGUMENTS_SPECIALIZATION	< RETURN_TYPE, Arg1Type, Arg2Type, Arg3Type, Arg4Type, Arg5Type, Arg6Type, Arg7Type, Arg8Type, NullType, NullType >

#include "Delegate_template.h"

// 9 arguments specialization

#undef RETURN_TYPE
#undef RETURN_TYPE_TEMPLATE
#undef COMMA_SEPARATOR
#undef ARGUMENTS_TYPES
#undef ARGUMENTS_TYPES_TEMPLATE
#undef ARGUMENTS_TYPES_NAMES
#undef ARGUMENTS_NAMES
#undef ARGUMENTS_SPECIALIZATION

#define RETURN_TYPE					ReturnType
#define RETURN_TYPE_TEMPLATE		typename RETURN_TYPE
#define COMMA_SEPARATOR				,
#define ARGUMENTS_TYPES				Arg1Type, Arg2Type, Arg3Type, Arg4Type, Arg5Type, Arg6Type, Arg7Type, Arg8Type, Arg9Type
#define ARGUMENTS_TYPES_TEMPLATE	typename Arg1Type, typename Arg2Type, typename Arg3Type, typename Arg4Type, typename Arg5Type, typename Arg6Type, typename Arg7Type, typename Arg8Type, typename Arg9Type
#define ARGUMENTS_TYPES_NAMES		Arg1Type a1, Arg2Type a2, Arg3Type a3, Arg4Type a4, Arg5Type a5, Arg6Type a6, Arg7Type a7, Arg8Type a8, Arg9Type a9
#define ARGUMENTS_NAMES				a1, a2, a3, a4, a5, a6, a7, a8, a9
#define ARGUMENTS_SPECIALIZATION	< RETURN_TYPE, Arg1Type, Arg2Type, Arg3Type, Arg4Type, Arg5Type, Arg6Type, Arg7Type, Arg8Type, Arg9Type, NullType >

#include "Delegate_template.h"

} //end namespace CORE
#endif
